package com.youlu.campus.service.ranking.impl;

import com.alibaba.fastjson.JSON;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.result.UpdateResult;
import com.youlu.campus.base.community.RankingList;
import com.youlu.campus.base.exception.BusinessException;

import com.youlu.campus.common.utils.RedisKeyUtil;
import com.youlu.campus.entity.ActivityInfo;
import com.youlu.campus.entity.UniversityActivityTaskRanking;
import com.youlu.campus.entity.UniversityInfo;
import com.youlu.campus.entity.UserPO;
import com.youlu.campus.entity.VO.RankingListQueryVO;
import com.youlu.campus.entity.VO.req.RankingListResVO;
import com.youlu.campus.entity.VO.req.UniversityRankQueryVO;
import com.youlu.campus.entity.system.UserDataPermission;
import com.youlu.campus.service.activity.ActivityService;
import com.youlu.campus.service.base.LockService;
import com.youlu.campus.service.rank.RankBaseService;
import com.youlu.campus.service.ranking.UniversityActivityRankingService;
import com.youlu.campus.service.system.UserDataPermissionService;
import com.youlu.campus.service.university.UniversityService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bson.Document;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class UniversityActivityRankingServiceImpl implements UniversityActivityRankingService {
    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private RankBaseService rankBaseService;
    @Autowired
    private UniversityService universityService;
    @Autowired
    private LockService lockService;
    @Lazy
    @Autowired
    private ActivityService activityService;
    @Lazy
    @Autowired
    private UserDataPermissionService userDataPermissionService;

    private static HashMap<String, ActivityInfo> activityInfoHashMap = new HashMap<>();

    /**
     * 更新高校排行榜
     *
     * @param userPO
     * @param activityId
     */
    @Override
    public void updateQuantity(UserPO userPO, String activityId, String universityId, Integer q) {
        log.info(":>>> 开始更新用户:{} 高校排行榜,universityId:{},activityId:{},{}", userPO == null ? null : userPO.getId(), universityId, activityId, q);
        String key = String.format("com.luyou.updateUniversity:%s:activityId:%s", userPO.getId(), activityId);
        try {
            boolean lock = lockService.tryLock(key, 60, TimeUnit.SECONDS);
            log.info(":>>> 获取用户:{} 活动:{} 高校排行榜锁结果:{}", userPO.getId(), activityId, lock);
            if (!lock) {
                lockService.unLock(key);
                return;
            }
            UniversityActivityTaskRanking db = this.find(universityId, activityId);
            if (Objects.isNull(db)) {
                log.info(":>>> 用户:{} 活动:{} 高校排行榜不存在新建", userPO.getId(), activityId);
                db = new UniversityActivityTaskRanking();
                db.setActivityId(activityId);
                UniversityInfo universityInfo = universityService.get(universityId);
                if (Objects.nonNull(universityInfo)) {
                    db.setImageUrl(universityInfo.getLogo());
                    db.setName(universityInfo.getName());
                }
                db.setQuantity(Double.valueOf(q + ""));
                db.setActivityId(activityId);
                db.setCreatedTime(new Date());
                db.setUniversityId(universityId);
                mongoTemplate.insert(db);
                rankBaseService.addSingleRank(getUniversityRankKey(activityId), universityId, Double.valueOf(q + ""));
                return;
            }
            log.info(":>>> 用户:{} 活动:{} 高校排行榜存在修改", userPO.getId(), activityId);
            boolean re = increQuantity(db.getId(), Double.valueOf(q + ""));
            rankBaseService.remove(getUniversityRankKey(activityId), universityId);
            rankBaseService.addSingleRank(getUniversityRankKey(activityId), universityId, Double.valueOf(q + "") + db.getQuantity());
            log.info(":>>> 用户活动高校排行榜存在更新:{},结果:{}", userPO.getId(), re);
        } catch (Exception e) {
            log.error(":>>> 更新用户活动高校排行榜错误:{}", e);
        } finally {
            lockService.unLock(key);
        }
    }

    @Override
    public RankingListResVO getUniversityRanking(RankingListQueryVO req, UserPO userPO) {
        log.info(":>>> 获取用户活动高校排行榜:{}", JSON.toJSONString(req));
        if (Objects.isNull(req) || StringUtils.isBlank(req.getActivityId())) {
            log.error(":>>> 获取活动高校排行榜参数错误=");
            throw new BusinessException("获取活动高校排行榜参数错误");
        }
        req.setUserPO(userPO);
        return getRankList(req, userPO);
    }

    /**
     * 获取用户积分总榜总榜
     *
     * @param req
     * @param userPO
     * @return
     */
    private RankingListResVO getRankList(RankingListQueryVO req, UserPO userPO) {
        RankingListResVO rankingListResVO = new RankingListResVO();
        try {
            if (Objects.isNull(req.getPageSize())) {
                req.setPageSize(10);
            }
            Query query = new Query();
            if (StringUtils.isNotBlank(req.getActivityId())) {
                query.addCriteria(Criteria.where("activityId").is(req.getActivityId()));
                log.info(":>>> 活动ID:{},任务ID:{}", req.getActivityId(), req.getTaskId());
            }
//            query.with(new Sort(Sort.Direction.DESC, "quantity"));
            Sort sort = Sort.by(Sort.Direction.DESC, "quantity");
            query.with(sort);
            query.limit(req.getPageSize());
            Long count = mongoTemplate.getCollection(mongoTemplate.getCollectionName(UniversityActivityTaskRanking.class)).countDocuments(query.getQueryObject());
            Integer totalPages = 0;
            if (count.intValue() == 0) {
                totalPages = 0;
            } else {
                if (count.intValue() % req.getPageSize() == 0) {
                    totalPages = count.intValue() / req.getPageSize();
                } else {
                    totalPages = count.intValue() / req.getPageSize() + 1;
                }
            }
            log.info(":>>> 用户高校排行榜数量:{},总页数:{},每页大小:{},当前页:{}", count, totalPages, req.getPageSize(), req.getPage());
            List<UniversityActivityTaskRanking> rankings = null;
            if (totalPages < req.getPage()) {
                rankings = new ArrayList<>();
                log.info(":>>> 排行榜总页数:{} 是最后一页:{}", totalPages, req.getPage());

            } else {
                query.skip((req.getPage() - 1) * req.getPageSize());
                MongoCursor iterable = mongoTemplate.getCollection(mongoTemplate.getCollectionName(UniversityActivityTaskRanking.class)).find(query.getQueryObject()).limit(req.getPageSize()).skip((req.getPage() - 1) * req.getPageSize()).
                        noCursorTimeout(true).sort(query.getSortObject()).batchSize(req.getPageSize()).iterator();
                if (Objects.isNull(rankings)) {
                    rankings = new ArrayList<>();
                }
                Document o = null;
                UniversityActivityTaskRanking r = null;
                while (iterable.hasNext()) {
                    o = (Document) iterable.next();
                    if (Objects.nonNull(o)) {
                        r = new UniversityActivityTaskRanking();
                        r.setId(o.getObjectId("id").toString());
                        r.setName(o.getString("name"));
                        r.setImageUrl(o.getString("imageUrl"));
                        r.setQuantity(o.getDouble("quantity"));
                        r.setUniversityId(o.getString("universityId"));
                        rankings.add(r);
                    }
                }

            }
            List<RankingList> rankingLists = new ArrayList<>();
            boolean in = false;
            int order = 1;
            int uOrder = 1001;
            int orderNN = (req.getPage() - 1) * req.getPageSize() + 1;
            List<String> uIds = new ArrayList<>();
            for (UniversityActivityTaskRanking d : rankings) {
                if (StringUtils.isNotBlank(d.getUniversityId())) {
                    uIds.add(d.getUniversityId());
                }
            }
            HashMap<String, UniversityInfo> uMap = universityService.getUniversitys(uIds);
            for (UniversityActivityTaskRanking d : rankings) {
                if (req.getUniversityId().equals(d.getUniversityId())) {
                    in = true;
                    uOrder = order;
                }
                RankingList rankingList = new RankingList();
                UniversityInfo universityInfo = uMap.get(d.getUniversityId());
                if (Objects.nonNull(universityInfo)) {
                    rankingList.setImageUrl(universityInfo.getLogo());
                    rankingList.setName(universityInfo.getName());
                }
                if (Objects.nonNull(d.getQuantity())) {
                    BigDecimal b = new BigDecimal(d.getQuantity());
                    Double q = b.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
                    rankingList.setQuantity(q);
                }
                rankingList.setOrderNo(orderNN);
                rankingLists.add(rankingList);
                order++;
                orderNN++;
            }
            rankingListResVO.setDatas(rankingLists);
            rankingListResVO.setInRanking(in);
            UniversityActivityTaskRanking u = this.find(req.getUniversityId(), req.getActivityId());
            if (Objects.isNull(u)) {
                rankingListResVO.setOrderNo(0);
                rankingListResVO.setQuantity(0.0D);
            } else {
                Long uO = rankBaseService.getSingleRankOrder(getUniversityRankKey(req.getActivityId()), req.getUniversityId());
                if (Objects.isNull(uO)) {
                    uO = 0L;
                }
                rankingListResVO.setOrderNo(uO.intValue());
                rankingListResVO.setQuantity(u.getQuantity());
            }
            rankingListResVO.setInRanking(in);
            rankingListResVO.setPage(req.getPage());
            rankingListResVO.setPageSize(req.getPageSize());
            rankingListResVO.setTotalPages(count.intValue() / req.getPageSize() + 1);
            log.info(":>>> 全国高校排行榜结果:{}, 排行榜大小:{},排行榜数据:{}", count.intValue() / req.getPageSize() + 1, rankingListResVO.getDatas() == null ? 0 : rankingListResVO.getDatas().size(), JSON.toJSONString(rankingListResVO));
        } catch (Exception e) {
            log.error(":>>> 获取高校排行榜错误:{}", e);
        }
        return rankingListResVO;
    }

    private UniversityActivityTaskRanking find(String universityId, String activityId) {
        Query queryU = new Query();
        queryU.addCriteria(Criteria.where("universityId").is(universityId).and(
                "activityId").is(activityId));
        return mongoTemplate.findOne(queryU, UniversityActivityTaskRanking.class);
    }

    /**
     * 获取高校排行榜Key
     *
     * @param activityId
     * @return
     */
    @Override
    public String getUniversityRankKey(String activityId) {
        return RedisKeyUtil.getUniversityRankPreKey(activityId);
    }

    @Override
    public Page<UniversityActivityTaskRanking> list(UniversityRankQueryVO req) {
        Query query = new Query();
        UserDataPermission userDataPermission = null;
        boolean hasUserId = StringUtils.isNotBlank(req.getUserId());
        List<String> aclAidList = new ArrayList<>();
        boolean choiced = false;
        if (hasUserId) {
            UserDataPermission reqP = new UserDataPermission();
            reqP.setUserId(req.getUserId());
            List<UserDataPermission> datas = userDataPermissionService.list(reqP);
            if (!CollectionUtils.isEmpty(datas) && !CollectionUtils.isEmpty(datas.get(0).getActivityId())) {
                userDataPermission = datas.get(0);
                log.info(":>>> 用户:{} 的数据权限权限配置:{}", req.getUserId(),
                        JSON.toJSON(userDataPermission.getActivityId()));
                aclAidList = datas.get(0).getActivityId();
            }
        }
        Criteria criteria = new Criteria();
        if (StringUtils.isNotEmpty(req.getActivityId())) {
            criteria.andOperator(Criteria.where("activityId").is(req.getActivityId()));
            query.addCriteria(criteria);
            choiced = true;
        } else {
            criteria.andOperator(Criteria.where("activityId").in(aclAidList));
            query.addCriteria(criteria);
        }
        if (StringUtils.isNotBlank(req.getUniversityId())) {
            query.addCriteria(Criteria.where("universityId").is(req.getUniversityId()));
            choiced = true;
        }
        query.addCriteria(Criteria.where("deleted").is(false));
        if (Objects.nonNull(req.getQStart()) && Objects.nonNull(req.getQEnd())) {
            Criteria criteriaQ = new Criteria();
            criteriaQ.andOperator(Criteria.where("q").gte(req.getQStart()), Criteria.where("q").lte(req.getQStart()));
            query.addCriteria(criteriaQ);
        } else if (Objects.nonNull(req.getQStart())) {
            query.addCriteria(Criteria.where("q").gte(req.getQStart()));
        } else if (Objects.nonNull(req.getQEnd())) {
            query.addCriteria(Criteria.where("q").lte(req.getQStart()));
        }
        long count = 100L;
        if (choiced) {
            count = mongoTemplate.count(query, UniversityActivityTaskRanking.class);
        } else {
            count = 20000L;
        }
        PageRequest pageRequest = PageRequest.of(req.getPage() - 1, req.getPageSize());
        Sort sort = Sort.by(Sort.Direction.DESC, "quantity");
        query.with(sort);
        List<UniversityActivityTaskRanking> ranks = mongoTemplate.find(query.with(pageRequest), UniversityActivityTaskRanking.class);
        for (UniversityActivityTaskRanking r : ranks) {
            if (StringUtils.isBlank(r.getActivityId())) {
                continue;
            }
            ActivityInfo activityInfo = this.getActivityInfo(r.getActivityId());
            if (Objects.nonNull(activityInfo)) {
                r.setActivityName(activityInfo.getName());
            }
        }
        Long total = count;
        return PageableExecutionUtils.getPage(ranks, pageRequest, () -> total);
    }

    @Override
    public UniversityActivityTaskRanking findOne(String id) {
        Query query = new Query();
        query.addCriteria(Criteria.where("id").is(id));
        return mongoTemplate.findOne(query, UniversityActivityTaskRanking.class);
    }

    @Override
    public boolean update(UniversityActivityTaskRanking req) {
        UniversityActivityTaskRanking rank = this.findOne(req.getId());
        ActivityInfo activityInfo = this.getActivityInfo(rank.getActivityId());
        if (!activityInfo.getCreatorId().equals(req.getOptUserId())) {
            log.error(":>>> 当前操作人不是活动创建者,optUserId:{},creatorId:{}", req.getOptUserId(), activityInfo.getCreatorId());
            throw new BusinessException("当前操作人不是活动创建者");
        }
        Query query = new Query();
        query.addCriteria(Criteria.where("id").is(req.getId()));
        Update update = new Update();
        boolean toUpdate = false;
        if (Objects.nonNull(req.getQuantity())) {
            update.set("quantity", req.getQuantity());
            toUpdate = true;
        }
        if (Objects.nonNull(req.getDeleted())) {
            update.set("deleted", req.getDeleted());
            toUpdate = true;
        }
        if (toUpdate) {
            return mongoTemplate.updateFirst(query, update, UniversityActivityTaskRanking.class).getModifiedCount()
                    > 0 ? true : false;
        }
        return false;
    }

    @Override
    public boolean delete(String id) {
        Query query = new Query().addCriteria(Criteria.where("id").is(id));
        mongoTemplate.remove(query, UniversityActivityTaskRanking.class);
        return mongoTemplate.remove(query, UniversityActivityTaskRanking.class).getDeletedCount() > 0 ? true : false;
    }

    private boolean increQuantity(String id, Double q) {
        Query query = new Query().addCriteria(Criteria.where("id").is(id));
        Update update = new Update();
        update.inc("quantity", Double.valueOf(q + ""));
        update.set("updatedTime", new Date());
        UpdateResult updateResult = mongoTemplate.updateFirst(query, update, UniversityActivityTaskRanking.class);
        return updateResult.getModifiedCount() > 0 ? true : false;
    }

    private ActivityInfo getActivityInfo(String activityId) {
        if (this.activityInfoHashMap.containsKey(activityId)) {
            return activityInfoHashMap.get(activityId);
        }
        ActivityInfo activityInfo = activityService.findOne(activityId);
        if (Objects.isNull(activityInfo)) {
            return null;
        }
        activityInfoHashMap.put(activityId, activityInfo);
        return activityInfo;
    }
}
